package com.kds.smarthome.service;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.kds.smarthome.common.StatisticsType;
import com.kds.smarthome.controller.vo.IndoorEnvironment;
import com.kds.smarthome.entity.HvacMonitorCfg;
import com.kds.smarthome.entity.IndoorEnvironmentCfg;
import com.kds.smarthome.mapper.FunctionMapper;
import com.mongodb.BasicDBObject;
import com.mongodb.client.MongoClient;
import lombok.extern.slf4j.Slf4j;
import org.bson.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory;
import org.springframework.data.mongodb.core.aggregation.*;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * @author 孔德嵩
 * 创建日期 2025/4/25
 */
@Service
@Slf4j
public class EnvironmentService {

    private static final String SMARTHOME_DB = "smarthome";
    private final Map<String, MongoTemplate> templateCache = new ConcurrentHashMap<>();
    @Autowired
    private FunctionMapper functionMapper;
    @Autowired
    private MongoClient mongoClient;

    /**
     * 获取指定数据库的 MongoTemplate
     */
    private MongoTemplate getMongoTemplate() {
        return templateCache.computeIfAbsent(EnvironmentService.SMARTHOME_DB, db -> new MongoTemplate(new SimpleMongoClientDatabaseFactory(mongoClient, db)));
    }

    /**
     * 查询室内环境历史数据
     *
     * @param buildingCode 建筑编码
     * @param startDate    开始时间
     * @param endDate      结束时间
     * @return 环境历史数据列表
     */
    public List<IndoorEnvironment> queryIndoorEnvironmentHistory(String buildingCode, Date startDate, Date endDate) {


        IndoorEnvironmentCfg indoorEnvironmentCfg = functionMapper.selectBuildingIndoorEnvironmentFids(buildingCode);

        String temperature = indoorEnvironmentCfg.getTemperature();

        String humidity = indoorEnvironmentCfg.getHumidity();


        // 3. 构建MongoDB查询
        MongoTemplate mongoTemplate = getMongoTemplate();


// 第一阶段：匹配条件 - fid在列表中且时间在范围内
        MatchOperation matchOperation = Aggregation.match(
                Criteria.where("fid").in(temperature,humidity)
                        .and("ts").gt(startDate).lte(endDate)
        );

// 第二阶段：按时间分组，为每个fid创建单独的字段
        ProjectionOperation projectOperation = Aggregation.project()
                .and("ts").as("date")
                .and("fid").as("fid")
                .and("value").as("value");

// 第三阶段：按时间分组，将fid作为列
        GroupOperation groupOperation = Aggregation.group("date")
                .first("date").as("date")
                .push(new BasicDBObject("fid", "$fid").append("value", "$value")).as("readings");

// 第四阶段：将数组转换为结果对象
        ProjectionOperation finalProjection = Aggregation.project()
                .and("date").as("date")
                .and("readings").as("readings");

// 第五阶段：按时间排序
        SortOperation sortOperation = Aggregation.sort(Sort.Direction.ASC, "date");

// 组合聚合管道
        Aggregation aggregation = Aggregation.newAggregation(
                matchOperation,
                projectOperation,
                groupOperation,
                finalProjection,
                sortOperation
        );

// 执行聚合查询
        AggregationResults<Document> aggregationResults = mongoTemplate.aggregate(
                aggregation, "data", Document.class);

        List<IndoorEnvironment> results = new ArrayList<>();

// 处理聚合结果
        for (Document doc : aggregationResults.getMappedResults()) {
            IndoorEnvironment environment = new IndoorEnvironment();
            environment.setRegion(buildingCode);
            environment.setDate((Date) doc.get("date"));

            // 处理每个时间点的读数
            List<Document> readings = (List<Document>) doc.get("readings");
            if (readings != null) {
                for (Document reading : readings) {
                    String fid = reading.getString("fid");
                    Object valueObj = reading.get("value");

                    // 安全地处理不同类型的数值
                    Double value = null;
                    if (valueObj != null) {
                        if (valueObj instanceof Double) {
                            value = (Double) valueObj;
                        } else if (valueObj instanceof Integer) {
                            value = ((Integer) valueObj).doubleValue();
                        } else if (valueObj instanceof Long) {
                            value = ((Long) valueObj).doubleValue();
                        } else if (valueObj instanceof String) {
                            try {
                                value = Double.parseDouble((String) valueObj);
                            } catch (NumberFormatException e) {
                                // 记录日志但继续处理
                                log.warn("Unable to parse value for fid {}: {}", fid, valueObj);
                            }
                        }
                    }

                    // 根据fid设置相应的字段
                    if (fid.equals(temperature)) {
                        environment.setTemperature(value);
                    } else if (fid.equals(humidity)) {
                        environment.setHumidity(value);
                    }
                }
            }

            results.add(environment);
        }

        return results;
    }

    /**
     * 查询当前室内环境数据
     *
     * @param buildingCode 建筑编码
     * @return 当前室内环境数据
     */
    public IndoorEnvironment queryIndoorEnvironmentCurrent(String buildingCode) {


        IndoorEnvironmentCfg indoorEnvironmentCfg = functionMapper.selectBuildingIndoorEnvironmentFids(buildingCode);

        String temperature = indoorEnvironmentCfg.getTemperature();

        String humidity = indoorEnvironmentCfg.getHumidity();

        String pm2p5 = indoorEnvironmentCfg.getPm2p5();

        String tvoc = indoorEnvironmentCfg.getTvoc();

        String co2 = indoorEnvironmentCfg.getCo2();

        // 3. 计算时间范围（最近1小时）
        Date now = new Date();

        DateTime start = DateUtil.offsetMinute(now, -60);

        //TODO
// 将需要查询的fid添加到列表中，过滤掉null值
        List<String> fids = Arrays.asList(temperature, humidity, pm2p5, tvoc,co2);

// 构建aggregation查询
// 第一阶段：匹配条件 - fid在列表中且时间在范围内
        MatchOperation matchOperation = Aggregation.match(Criteria.where("fid").in(fids).and("ts").gt(start.toJdkDate()).lte(now));

// 第二阶段：按fid分组，取最新的一条记录
        GroupOperation groupOperation = Aggregation.group("fid").last("value").as("value").last("ts").as("ts");

// 组合聚合管道
        Aggregation aggregation = Aggregation.newAggregation(matchOperation, groupOperation);

// 执行聚合查询
        AggregationResults<Document> results = getMongoTemplate().aggregate(aggregation, "data", Document.class);

// 创建结果对象
        IndoorEnvironment environment = new IndoorEnvironment();
        environment.setRegion(buildingCode);
        environment.setDate(now);


// 处理聚合结果
        for (Document doc : results.getMappedResults()) {
            String fid = doc.getString("_id");  // 分组键存储在_id字段
            Object valueObj = doc.get("value");
            Double value = null;

            // 安全地处理不同类型的数值
            if (valueObj != null) {
                if (valueObj instanceof Double) {
                    value = (Double) valueObj;
                } else if (valueObj instanceof Integer) {
                    value = ((Integer) valueObj).doubleValue();
                } else if (valueObj instanceof Long) {
                    value = ((Long) valueObj).doubleValue();
                } else if (valueObj instanceof String) {
                    try {
                        value = Double.parseDouble((String) valueObj);
                    } catch (NumberFormatException e) {
                        // 记录日志但继续处理
                        log.warn("Unable to parse value for fid {}: {}", fid, valueObj);
                    }
                }
            }

            // 根据fid设置相应的字段
            if (fid.equals(temperature)) {
                environment.setTemperature(value);
            } else if (fid.equals(co2)) {
                environment.setCo2(value.intValue());
            }else if (fid.equals(humidity)) {
                environment.setHumidity(value);
            } else if (fid.equals(pm2p5)) {
                environment.setPm2p5(value != null ? value.intValue() : null);
            } else if (fid.equals(tvoc)) {
                environment.setTvoc(value != null ? value.intValue() : null);
            }
        }
        return environment;

    }

    public HvacMonitorCfg getHvacMonitorCfg(String buildingCode) {

        return functionMapper.selectHvacMonitorCfg(buildingCode);
    }

    public Map<String, Object> currentHVAC(String buildingCode) {

        List<String> fids = functionMapper.findFidsByBuildingCodeAndTypes(buildingCode, null, null, StatisticsType.DIRECT.getCode());


        Date now = DateUtil.date();
        Date thirtyMinutesAgo = DateUtil.offsetMinute(now, -30);

        // 构建查询条件
        MatchOperation matchOperation = Aggregation.match(Criteria.where("fid").in(fids).and("ts").gt(thirtyMinutesAgo).lte(now));

        // 按fid分组
        GroupOperation groupOperation = Aggregation.group("fid").last("value").as("latestValue");

        // 设置返回字段
        ProjectionOperation projectionOperation = Aggregation.project().and("_id").as("fid").and("latestValue").as("value");

        // 组合聚合操作 - 移除了排序操作
        Aggregation aggregation = Aggregation.newAggregation(matchOperation, groupOperation, projectionOperation);

        // 执行聚合操作，直接使用Map<String, Object>
        AggregationResults<Map> results = getMongoTemplate().aggregate(aggregation, "data", Map.class);

        // 转换结果为所需的Map格式
        Map<String, Object> resultMap = new HashMap<>();
        for (Map doc : results.getMappedResults()) {
            resultMap.put((String) doc.get("fid"), doc.get("value"));
        }

        return resultMap;

    }

    public List<Map<String, Object>> getHVACHistoryByFid(String fid, Date startDate, Date endDate) {

        Criteria criteria=Criteria.where("fid").is(fid).and("ts").gte(startDate).lte(endDate);

        Sort sort = Sort.by(Sort.Direction.ASC, "ts");

        Query query=Query.query(criteria);

        query.with(sort);

        // 设置投影，只返回ts和value字段，不返回_id
        query.fields().include("ts").include("value").exclude("_id");

        return getMongoTemplate().find(query, HashMap.class,"data")
                .stream()
                .map(document -> {
// 确保返回的是Map<String, Object>类型
                    Map<String, Object> resultMap = new HashMap<>();
                    resultMap.put("ts", document.get("ts"));
                    resultMap.put("value", document.get("value"));
                    return resultMap;
                })
                .collect(Collectors.toList());
    }
}
